home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
pcb121.zip
/
I8250.INC
< prev
next >
Wrap
Text File
|
1992-01-23
|
17KB
|
443 lines
;;*****************************************************************************
;; i8250.inc i8250.inc
;;*****************************************************************************
;;
;; Copyright (C) 1990 Vance Morrison
;;
;;
;; Permission to view, compile, and modify for LOCAL (intra-organization)
;; USE ONLY is hereby granted, provided that this copyright and permission
;; notice appear on all copies. Any other use by permission only.
;;
;; Vance Morrison makes no representations about the suitability
;; of this software for any purpose. It is provided "as is" without expressed
;; or implied warranty. See the copywrite notice file for complete details.
;;
;;*****************************************************************************
;; this software essentially is responsible for setup of the 8250 and
;; getting individual chacters off the serial line. The routines here
;; where designed so that they will work with the 16550AF and take advantage
;; of that chip's read and write FIFOs.
;;
;; The functions provided by this file are
;;
;; I8250_DECLARE name, port_prefix, port, io_addr, irq, flow
;; I8250_DEFINE_in_BX name, fail
;; I8250_WRITE_in_AL_const_BX_BP_ES MACRO name
;; I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES MACRO name not_ready
;;
;; the following 'upcall' routines will be called from interupt level
;; <port_prefix>_CONSUME_in_AL_const_BX_BP_ES 'port'
;; <port_prefix>_WRITE_EMPTY_const_BX_BP_ES 'port'
;;
;; i8250_&name&_declared ;; one if this interface exists
;; i8250_declared ;; one if ANY interface exists
;;
;;******************************************************************************
;; 'standard' port numbers
IBM_COM1_PORT = 3F8H
IBM_COM2_PORT = 2F8H
IBM_COM3_PORT = 3E8H
IBM_COM4_PORT = 2E8H
;; and interupts
IBM_COM1_IRQ = 4
IBM_COM2_IRQ = 3
;; the 10 registers of the 8250
I8250_REG_THR = 0 ;; trans hold reg (LCR_DIVISOR = 0) (write only)
I8250_REG_RDR = 0 ;; rec data reg (LCR_DIVISOR = 0) (read only)
I8250_REG_DIVL = 0 ;; baud rate divisor (LCR_DIVISOR = 1)
I8250_REG_DIVH = 1 ;; baud rate divisor (LCR_DIVISOR = 1)
I8250_REG_IER = 1 ;; interupt enable (LCR_DIVISOR = 0)
I8250_REG_IIR = 2 ;; interupt ident (read only)
NS16550_REG_FIFO = 2 ;; FIFO control register for the NS16550AF chip
I8250_REG_LCR = 3 ;; line control
I8250_REG_MCR = 4 ;; modem control
I8250_REG_LSR = 5 ;; line status
I8250_REG_MSR = 6 ;; modem status
NS16550_REG_SCR = 7 ;; Scratch register.
;; masks for the IER
I8250_IER_DIN = 1H ;; interupt on data in
I8250_IER_DOUT = 2H ;; interupt on data ready for out
I8250_IER_ERR = 4H ;; interupt on data error
I8250_IER_MODEM = 8H ;; interupt on modem status change
;; masks for the IIR
I8250_IIR_NOINT = 1H ;; the rest of IIR valid only if this bit is 0
I8250_IIR_TYPE = 6H ;; type of highest priority interupt
NS16550_IIR_CHECK = 0C0H ;; Both of these bits must be 1 if its a 16550AF
;; values for type field of the IIR (read only)
I8250_TYPE_MODEM = 0H ;; notice they are shifted to the TYPE field
I8250_TYPE_DOUT = 2H
I8250_TYPE_DIN = 4H
I8250_TYPE_ERR = 6H
;; masks for the FIFO reg (write only)
NS16550_FIFO_ON = 1 ;; Enable the FIFOs
NS16550_FIFO_FIN = 2 ;; Flush input FIFO
NS16550_FIFO_FOUT = 4 ;; Flush input FIFO
NS16550_FIFO_TRIG = 0C0H ;; bytes in input FIFO before interupt
;; values for the TRIG field of the FIFO reg
NS16550_TRIG_1 = 000H ;; trigger on 1 byte
NS16550_TRIG_4 = 040H ;; trigger on 4 bytes
NS16550_TRIG_8 = 080H ;; trigger on 8 bytes
NS16550_TRIG_14 = 0C0H ;; trigger on 14 bytes
;; masks for the LCR
I8250_LCR_BITS = 3H ;; number of bits - 5
I8250_LCR_STOP = 4H
I8250_LCR_PARITY = 38H
I8250_LCR_BREAK = 40H ;; send a break
I8250_LCR_DIVISOR = 80H ;; make reg 0,1 be the divisor register
;; values for the parity field of the LCR
I8250_PARITY_IGN = 0H ;; notice they are shifted
I8250_PARITY_ODD = 20H
I8250_PARITY_EVEN = 30H
I8250_PARITY_MARK = 28H
I8250_PARITY_SPACE = 38H
;; masks for the MCR
I8250_MCR_DTR = 1H ;; activate DTR line
I8250_MCR_RTS = 2H ;; activate RTS line
I8250_MCR_OUT1 = 4H ;; out1 Line
I8250_MCR_OUT2 = 8H ;; out2 line
I8250_MCR_LOOP = 10H ;; loopback
;; masks for the LSR
I8250_LSR_DIN = 1H ;; data in read register
I8250_LSR_OVR = 2H ;; data overun
I8250_LSR_PERR = 4H ;; data parity error
I8250_LSR_FERR = 8H ;; data framing error
I8250_LSR_BREAK = 10H ;; break detected
I8250_LSR_DOUT = 20H ;; Transmit buffer empty
I8250_LSR_TSR = 40H ;; Transmit shift reg empty (no data on line)
;; masks for the MSR
I8250_MSR_DELTA_CTS = 1H ;; CTS changed
I8250_MSR_DELTA_DSR = 2H ;; DSR changed
I8250_MSR_DELTA_RI = 4H ;; RI changed
I8250_MSR_DELTA_DCD = 8H ;; DCD changed
I8250_MSR_CTS = 10H ;; CTS active
I8250_MSR_DSR = 20H ;; DSR active
I8250_MSR_RI = 40H ;; RI active
I8250_MSR_DCD = 80H ;; DCD active
;;*****************************************************************************
;; defs for the I8259 programable interupt controller
IBM_I8259 = 20H ;; standard place on the IBM PC
I8259_REG_CNTRL = 0 ;; the 8259 control register
I8259_REG_MASKS = 1 ;; the interupt mask register
;; control register functions
I8259_CNTRL_EOI = 20H ;; the End of Interupt command
;;*****************************************************************************
;; I8250_DECLARE declares your intent to use a 8250 serial controler at
;; address 'io_addr', using interupt 'irq'. When it gets characters it calls
;; <port_prefix>_CONSUME_in_AL_const_BX_BP_ES 'port' and
;; <port_prefix>_WRITE_EMPTY_const_BX_CX_DX_BP_SI_DI_ES 'port' when it
;; can write characters. If 'flow' is non-blank and = 1
;; then hardware flow control using RTS-CTS is followed.
;;
I8250_DECLARE MACRO name, port_prefix, port, io_addr, irq, flow
.errb <name>
.errb <irq>
.DATA
i8250_declared = name
i8250_&name&_port = port
i8250_&name&_prefix equ <port_prefix>
i8250_&name&_declared = 1
i8250_&name&_io = io_addr
i8250_&name&_irq = irq
i8250_&name&_flow = 0
ifnb <flow>
i8250_&name&_flow = 0&flow
endif
.CODE
global i8250_data_seg:word
global i8250_&name&_real_define_in_BX_out_AX:near
ENDM
;;******************************************************************************
;; I8250_DEFINE name, fail
;; sets asside memory an name object and initializes it. This
;; routine is a no-op if 'name' was not declared. It jumps to 'fail'
;; if there was an error in setup. BX contains the word that
;; is the baud rate divisor. (12 = 9600Baud, 6 = 19200Baud, etc)
;;
I8250_DEFINE_in_BX MACRO name, fail
.errb <name>
.errb <fail>
ifdef i8250_&name&_declared
call i8250_&name&_real_define_in_BX_out_AX
or AX, AX
jnz fail
endif
ENDM
;;*************************************************************************
;; I8250_REAL_DEFINE is simply a the code that I8250_DEFINE jumps to.
;; this indirection is necessary to avoid macros getting too large.
;;
I8250_REAL_DEFINE MACRO name
local done
.errb <name>
ifdef i8250_&name&_declared
i8250_&name&_real_define_in_BX_out_AX:
mov word ptr CS:i8250_data_seg, DS ;; make sure interupt routine OK
;; turn off my interupt
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8259_REG_MASKS, IBM_I8259
and AL, (1 shl i8250_&name&_irq)
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8259_REG_MASKS, IBM_I8259
I8250_SETUP_in_BX_out_AX name, done
;; turn on my interupt
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8259_REG_MASKS, IBM_I8259
and AL, (not (1 shl i8250_&name&_irq))
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8259_REG_MASKS, IBM_I8259
sti ;; turn on interupts, if not already on
xor AX, AX ;; return success
done:
ret
if i8250_declared eq name ; define once for all
;;*****************************************************************
;; define the interupt handler
.CODE
i8250_data_seg: DW 0 ;; this location hold the data segment
i8250_interupt: ;; the actual interupt handler
cli
push DS
push AX
push CX
push DX
push SI
push DI
mov DS, word ptr CS:i8250_data_seg
IRP idx, <1,2,3,4,5,6,7,8> ;; check every chip
I8250_IF_CHECK_const_BX_BP_ES idx
endm
mov AL, I8259_CNTRL_EOI ; tell the 8259 we are done
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8259_REG_CNTRL, IBM_I8259
pop DI
pop SI
pop DX
pop CX
pop AX
pop DS
iret ; interupts flag restored on return
endif
endif
ENDM
;;******************************************************************************
;; I8250_SETUP does all the 'per chip' setup for a 8250 serial chip associated
;; with 'name'. if there is a failure 'fail' is jumped to, AX will have a
;; non-zero value if the 'fail' branch is taken. BX contains the word that
;; is the baud rate divisor. (12 = 9600Baud, 6 = 19200Baud)
;;
I8250_SETUP_in_BX_out_AX MACRO name, fail
local done, is_a_16550AF, is_a_8250
ifdef i8250_&name&_declared
; check to see that a serial port is there by reading 8250 register
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_LSR, i8250_&name&_io
cmp AL, 0FFH ;; non-existant registers turn up as all 1s
jz fail
;; set the baud rate divisor
mov AL, I8250_LCR_DIVISOR
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_LCR,i8250_&name&_io
mov AX, BX ;; get the baud rate
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_DIVL, i8250_&name&_io
mov AL, AH
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_DIVH,i8250_&name&_io
mov AL, (8-5) + I8250_PARITY_IGN ;; 8 bits no parity 1 stop bit
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_LCR, i8250_&name&_io
if i8250_&name&_flow eq 1
mov AL, I8250_IER_DIN + I8250_IER_DOUT + I8250_IER_MODEM
else
mov AL, I8250_IER_DIN + I8250_IER_DOUT
endif
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_IER, i8250_&name&_io
;; Turn on FIFO mode if this is a 16550AF
mov AL, NS16550_FIFO_ON+NS16550_FIFO_FIN+NS16550_FIFO_FOUT+NS16550_TRIG_8
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES NS16550_REG_FIFO,i8250_&name&_io
;; make sure that this is a 16550AF not just a 16550 (which has a bug)
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_IIR, i8250_&name&_io
and AL, NS16550_IIR_CHECK
test AL, 80H ;; is it a 16550?
jz is_a_8250
test AL, 40H ;; is it a 16550AF?
jnz is_a_16550AF
mov AL, 0 ;; turn off FIFO, the 16550 is buggy
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES NS16550_REG_FIFO,i8250_&name&_io
is_a_16550AF:
is_a_8250:
;; set DTR and RTS for those modems that care
;; ON AN IBM PC OUT2 MUST BE SET FOR PROPER OPERATION
mov AL, I8250_MCR_DTR + I8250_MCR_RTS + I8250_MCR_OUT2
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_MCR, i8250_&name&_io
cli
xor AX, AX ;; load the interupt vector
mov ES, AX
mov DI, (i8250_&name&_irq+8)*4
mov BX, offset i8250_interupt
mov AX, CS
mov ES:[DI], BX
mov ES:[DI+2], AX
sti
; acknowledge any interupts that may have occured in the past
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_RDR, i8250_&name&_io
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_MSR, i8250_&name&_io
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_IIR, i8250_&name&_io
endif
ENDM
;;******************************************************************************
;; I8250_IF_CHECK checks to see of interface 'name' has any characters to write
;; or to read and does the appropriate upcall if there is anthing to do.
;;
I8250_IF_CHECK_const_BX_BP_ES MACRO name
local done, try_write, try_read, done_write
ifdef i8250_&name&_declared
try_read:
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_LSR, i8250_&name&_io
and AL, I8250_LSR_DIN ;; is there a character ready?
jz try_write
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_RDR, i8250_&name&_io
UPCALL_CONSUME_in_AL_const_BX_BP_ES %i8250_&name&_prefix, %i8250_&name&_port
jmp try_read ;; keep trying until failure (for 16650)
try_write:
; acknowledge the Tran Empty interupt
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_IIR, i8250_&name&_io
I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES name, done_write
UPCALL_WRITE_EMPTY_const_BX_BP_ES %i8250_&name&_prefix,%i8250_&name&_port
done_write:
;; have all the interupts been processed?
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_IIR, i8250_&name&_io
test AL, I8250_IIR_NOINT
jz try_read ;; no, then try again until they are
;; Note that this final check for interupts is absolutely
;; necessary because the 8259 responds to EDGES, not LEVELs
;; only if we are absolutely sure the interupt line has gone
;; low (which would be the case if IIR has NOINT bit = 1) can
;; we be sure that the next interupt will generate an EDGE
;; the the 8259 will respond to. Without this check it is
;; possible for interupts from the 8250 to be lost causing
;; the code to hang
done:
endif
ENDM
UPCALL_CONSUME_in_AL_const_BX_BP_ES MACRO prefix, port
prefix&_CONSUME_in_AL_const_BX_BP_ES port
ENDM
UPCALL_WRITE_EMPTY_const_BX_BP_ES MACRO prefix, port
prefix&_WRITE_EMPTY_const_BX_BP_ES port
ENDM
;;******************************************************************************
;; W_CAN_WRITE jumps to 'not_ready' if the 8250 chip associated with
;; 'name' is not ready to recieve another byte. Otherwise it just returns.
;;
I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES MACRO name, not_ready
.errb <name>
.errb <success>
;; if we are doing flow control, we can only send if CTS is active
if i8250_&name&_flow eq 1
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_MSR, i8250_&name&_io
and AL, I8250_MSR_CTS
jz not_ready
endif
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_LSR, i8250_&name&_io
and AL, I8250_LSR_DOUT ;; is there a character ready?
jz not_ready
ENDM
;;******************************************************************************
;; WRITE_in_AL writes the character in AL to the chip 'name'. It
;; will busy wait if necessary, however this routine is guarenteed not
;; to busy wait if it is called only when the WRITE_EMPTY upcall is issued.
;;
I8250_WRITE_in_AL_const_BX_BP_ES MACRO name
local wait_loop, ready
.errb <name>
mov SI, AX ;; save AL
xor CX, CX
wait_loop:
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES I8250_REG_LSR, i8250_&name&_io
and AL, I8250_LSR_DOUT ;; is there a character ready?
jnz ready
dec CX ;; so we don't wait forever
jnz wait_loop
ready:
mov AX, SI ;; restore AL
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES I8250_REG_THR, i8250_&name&_io
ENDM
;;******************************************************************************
;; utility functions needed only within this module
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES MACRO port, if_io
mov DX, if_io+port
in AL, DX ;; AL contains data read from port
ENDM
;;******************************************************************************
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES MACRO port, if_io
mov DX, if_io+port
out DX, AL ;; AL contains data read from port
ENDM